#include "VertexIndexData.h"
#include "HardwareBufferManager.h"
#include "HardwareVertexBuffer.h"
#include "HardwareIndexBuffer.h"
#include "Vector3.h"
#include "AxisAlignedBox.h"
#include "Root.h"
#include "RenderSystem.h"
#include "Exception.h"

namespace PVM
{
	VertexData::VertexData(HardwareBufferManagerBase* mgr)
	{
		mMgr = mgr ? mgr : HardwareBufferManager::getSingletonPtr();
		vertexBufferBinding = mMgr->createVertexBufferBinding();
		vertexDeclaration = mMgr->createVertexDeclaration();
		mDeleteDclBinding = true;
		vertexCount = 0;
		vertexStart = 0;
		hwAnimDataItemsUsed = 0;
	}
	
	VertexData::VertexData(VertexDeclaration* dcl, VertexBufferBinding* bind)
	{
		mMgr = HardwareBufferManager::getSingletonPtr();
		vertexDeclaration = dcl;
		vertexBufferBinding = bind;
		mDeleteDclBinding = false;
		vertexCount = 0;
		vertexStart = 0;
		hwAnimDataItemsUsed = 0;
	}
	
	VertexData::~VertexData()
	{
		if (mDeleteDclBinding)
		{
			mMgr->destroyVertexBufferBinding(vertexBufferBinding);
			mMgr->destroyVertexDeclaration(vertexDeclaration);
		}
	}
	
	VertexData* VertexData::clone(bool copyData, HardwareBufferManagerBase* mgr) const
	{
		HardwareBufferManagerBase* pManager = mgr ? mgr : mMgr;
		
		VertexData* dest = new VertexData(mgr);
		
		const VertexBufferBinding::VertexBufferBindingMap& bindings = 
		this->vertexBufferBinding->getBindings();
		VertexBufferBinding::VertexBufferBindingMap::const_iterator vbi, vbend;
		vbend = bindings.end();
		for (vbi = bindings.begin(); vbi != vbend; ++vbi)
		{
			HardwareVertexBufferSharedPtr srcbuf = vbi->second;
			HardwareVertexBufferSharedPtr dstBuf;
			if (copyData)
			{
				dstBuf = pManager->createVertexBuffer(
				srcbuf->getVertexSize(), srcbuf->getNumVertices(), srcbuf->getUsage(),
													  srcbuf->hasShadowBuffer());
				
				dstBuf->copyData(*srcbuf, 0, 0, srcbuf->getSizeInBytes(), true);
			}
			else 
			{
				dstBuf = srcbuf;
			}
			
			dest->vertexBufferBinding->setBinding(vbi->first, dstBuf);
		}
		
		dest->vertexStart = this->vertexStart;
		dest->vertexCount = this->vertexCount;
		const VertexDeclaration::VertexElementList elems = 
		this->vertexDeclaration->getElements();
		VertexDeclaration::VertexElementList::const_iterator ei, eiend;
		eiend = elems.end();
		for (ei = elems.begin(); ei != eiend; ++ei)
		{
			dest->vertexDeclaration->addElement(
			ei->getSource(),
			ei->getOffset(),
			ei->getType(),
			ei->getSemantic(),
			ei->getIndex());
		}
		
		dest->hardwareShadowVolWBuffer = hardwareShadowVolWBuffer;
		
		dest->hwAnimationDataList = hwAnimationDataList;
		dest->hwAnimDataItemsUsed = hwAnimDataItemsUsed;
		
		return dest;
	}
	
	void VertexData::prepareForShadowVolume(void)
	{
		RenderSystem* rend = Root::getSingleton().getRenderSystem();
		bool useVertexPrograms = false;
		if (rend && rend->getCapabilities()->hasCapability(RSC_VERTEX_PROGRAM))
		{
			useVertexPrograms = true;
		}
		
		const VertexElement* posElem = vertexDeclaration->findElementBySemantic(VES_POSITION);
		if (posElem)
		{
			size_t v;
			unsigned short posOldSource = posElem->getSource();
			
			HardwareVertexBufferSharedPtr vbuf = vertexBufferBinding->getBuffer(posOldSource);
			bool wasSharedBuffer = false;
			
			if (vbuf->getVertexSize() > posElem->getSize())
			{
				wasSharedBuffer = true;
			}
			HardwareVertexBufferSharedPtr newPosBuffer, newRemainderBuffer;
			if (wasSharedBuffer)
			{
				newRemainderBuffer = vbuf->getManager()->createVertexBuffer(
				vbuf->getVertexSize() - posElem->getSize(), vbuf->getNumVertices(), vbuf->getUsage(),
																			vbuf->hasShadowBuffer());
			}
			
			size_t oldVertexCount = vbuf->getNumVertices();
			size_t newVertexCount = oldVertexCount * 2;
			newPosBuffer = vbuf->getManager()->createVertexBuffer(
																  VertexElement::getTypeSize(VET_FLOAT3), newVertexCount, vbuf->getUsage(),
																  vbuf->hasShadowBuffer());
			
			float* pSrc;
			unsigned char* pBaseSrc = static_cast<unsigned char*>
			(vbuf->lock(HardwareBuffer::HBL_READ_ONLY));
			
			float* pDest = static_cast<float*>(newPosBuffer->lock(HardwareBuffer::HBL_DISCARD));
			float* pDest2 = pDest + oldVertexCount * 3;
			size_t prePosVertexSize = 0, postPosVertexSize, postPosVertexOffset;
			unsigned char* pBaseDestRem = 0;
			if (wasSharedBuffer)
			{
				pBaseDestRem = static_cast<unsigned char*>(
														   newRemainderBuffer->lock(HardwareBuffer::HBL_DISCARD));
				prePosVertexSize = posElem->getOffset();
				postPosVertexOffset = prePosVertexSize + posElem->getSize();
				postPosVertexSize = vbuf->getVertexSize() - postPosVertexOffset;
				assert(newRemainderBuffer->getVertexSize() == prePosVertexSize + postPosVertexSize);
				
				for (v = 0; v < oldVertexCount; ++v)
				{
					posElem->baseVertexPointerToElement(pBaseSrc, &pSrc);
					*pDest++ = *pDest2++ = *pSrc++;
					*pDest++ = *pDest2++ = *pSrc++;
					*pDest++ = *pDest2++ = *pSrc++;
					
					if (prePosVertexSize > 0)
						memcpy(pBaseDestRem, pBaseSrc, prePosVertexSize);
					if (postPosVertexSize > 0)
						memcpy(pBaseDestRem + prePosVertexSize,
							   pBaseSrc + postPosVertexOffset, postPosVertexSize);
					pBaseDestRem += newRemainderBuffer->getVertexSize();
					
					pBaseSrc += vbuf->getVertexSize();
				}
			}
			else
			{
				memcpy(pDest, pBaseSrc, vbuf->getSizeInBytes());
				memcpy(pDest2, pBaseSrc, vbuf->getSizeInBytes());
			}
			
			vbuf->unlock();
			newPosBuffer->unlock();
			if (wasSharedBuffer)
				newRemainderBuffer->unlock();
			
			vbuf->getManager()->_forceReleaseBufferCopies(vbuf);
			if (useVertexPrograms)
			{
				hardwareShadowVolWBuffer = vbuf->getManager()->createVertexBuffer(
																				  sizeof(float), newVertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
				pDest = static_cast<float*>(
				hardwareShadowVolWBuffer->lock(HardwareBuffer::HBL_DISCARD));
				for (v = 0; v < oldVertexCount; ++v)
				{
					*pDest++ = 1.0f;
				}
				for (v = 0; v < oldVertexCount; ++v)
				{
					*pDest++ = 0.0f;
				}
				hardwareShadowVolWBuffer->unlock();
			}
			
			unsigned short newPosBufferSource;
			if (wasSharedBuffer)
			{
				newPosBufferSource = vertexBufferBinding->getNextIndex();
				vertexBufferBinding->setBinding(posOldSource, newRemainderBuffer);
			}
			else
			{
				newPosBufferSource = posOldSource;
			}
			vertexBufferBinding->setBinding(newPosBufferSource, newPosBuffer);
			
			VertexDeclaration::VertexElementList::const_iterator elemi = 
			vertexDeclaration->getElements().begin();
			VertexDeclaration::VertexElementList::const_iterator elemiend = 
			vertexDeclaration->getElements().end();
			unsigned short idx;
			for (idx = 0; elemi != elemiend; ++elemi, ++idx)
			{
				if (&(*elemi) == posElem)
				{
					vertexDeclaration->modifyElement(
					idx,
					newPosBufferSource,
					0,
					VET_FLOAT3,
					VES_POSITION);
				}
				else if (wasSharedBuffer &&
						 elemi->getSource() == posOldSource &&
						 elemi->getOffset() > prePosVertexSize)
				{
					vertexDeclaration->modifyElement(
					idx,
					posOldSource,
					elemi->getOffset() - posElem->getSize(),
					elemi->getType(),
					elemi->getSemantic(),
					elemi->getIndex());
				}
			}
		}
	}
	
	void VertexData::reorganiseBuffers(VertexDeclaration* newDeclaration,
				const BufferUsageList& bufferUsages, HardwareBufferManagerBase* mgr)
	{
		HardwareBufferManagerBase* pManager = mgr ? mgr : mMgr;
		
		newDeclaration->closeGapsInSource();
		unsigned short buf = 0;
		vector<void*> oldBufferLocks;
		vector<size_t> oldBufferVertexSizes;
		vector<void*> newBufferLocks;
		vector<size_t> newBufferVertexSizes;
		VertexBufferBinding* newBinding = pManager->createVertexBufferBinding();
		const VertexBufferBinding::VertexBufferBindingMap& oldBindingMap = vertexBufferBinding->getBindings();
		VertexBufferBinding::VertexBufferBindingMap::const_iterator itBinding;
		
		if (!oldBindingMap.empty())
		{
			size_t count = oldBindingMap.rbegin()->first + 1;
			oldBufferLocks.resize(count);
			oldBufferVertexSizes.resize(count);
		}
		
		for (itBinding = oldBindingMap.begin(); itBinding != oldBindingMap.end(); ++itBinding)
		{
			assert(itBinding->second->getNumVertices() >= vertexCount);
			
			oldBufferVertexSizes[itBinding->first] = 
			itBinding->second->getVertexSize();
			oldBufferLocks[itBinding->first] = 
			itBinding->second->lock(
									HardwareBuffer::HBL_READ_ONLY);
		}
		
		buf = 0;
		while (!newDeclaration->findElementsBySource(buf).empty())
		{
			size_t vertexSize = newDeclaration->getVertexSize(buf);
			
			HardwareVertexBufferSharedPtr vbuf =
			pManager->createVertexBuffer(
			vertexSize,
			vertexCount,
			bufferUsages[buf]);
			newBinding->setBinding(buf, vbuf);
			
			newBufferVertexSizes.push_back(vertexSize);
			newBufferLocks.push_back(
									 vbuf->lock(HardwareBuffer::HBL_DISCARD));
			buf++;
		}
		
		typedef map<const VertexElement*, const VertexElement*> NewToOldElementMap;
		NewToOldElementMap newToOldElementMap;
		const VertexDeclaration::VertexElementList& newElementList = newDeclaration->getElements();
		VertexDeclaration::VertexElementList::const_iterator ei, eiend;
		eiend = newElementList.end();
		for (ei = newElementList.begin(); ei != eiend; ++ei)
		{
			const VertexElement* oldElem = 
			vertexDeclaration->findElementBySemantic(
			(*ei).getSemantic(), (*ei).getIndex());
			if (!oldElem)
			{
				PVM_EXCEPT("Element not found in old vertex declaration", "VertexData::reorganiseBuffers");
			}
			newToOldElementMap[&(*ei)] = oldElem;
		}
		
		for (size_t v = 0; v < vertexCount; ++v)
		{
			for (ei = newElementList.begin(); ei != eiend; ++ei)
			{
				const VertexElement* newElem = &(*ei);
				newToOldElementMap::iterator noi = newToOldElementMap.find(newElem);
				const VertexElement* oldElem = noi->second;
				unsigned short oldBufferNo = oldElem->getSource();
				unsigned short newBufferNo = newElem->getSource();
				void* pSrcBase = static_cast<void*>(
				static_cast<unsigned char*>(oldBufferLocks[oldBufferNo]) +
													v * oldBufferVertexSizes[oldBufferNo]);
				void* pDstBase = static_cast<void*>(
				static_cast<unsigned char*>(newBufferLocks[newBufferNo])
													+ v * newBufferVertexSizes[newBufferNo]);
				void* pSrc, *pDst;
				oldElem->baseVertexPointerToElement(pSrcBase, &pSrc);
				newElem->baseVertexPointerToElement(pDstBase, &pDst);
				
				memcpy(pDst, pSrc, newElem->getSize());
			}
		}
		
		for (itBinding = oldBindingMap.begin(); itBinding != oldBindingMap.end(); ++itBinding)
		{
			itBinding->second->unlock();
		}
		
		for (buf = 0; buf < newBinding->getBufferCount(); ++buf)
		{
			newBinding->getBuffer(buf)->unlock();
		}
		
		if (mDeleteDclBinding)
		{
			pManager->destroyVertexBufferBinding(vertexBufferBinding);
			pManager->destroyVertexDeclaration(vertexDeclaration);
		}
		
		vertexDeclaration = newDeclaration;
		vertexBufferBinding = newBinding;
		
		mMgr = pManager;
		mDeleteDclBinding = true;
	}
	
	void VertexData::reorganiseBuffers(VertexDeclaration* newDeclaration, HardwareBufferManagerBase* mgr)
	{
		BufferUsageList usages;
		for (unsigned short b = 0; b <= newDeclaration->getMaxSource(); ++b)
		{
			VertexDeclaration::VertexElementList destElems = newDeclaration->findElementsBySource(b);
			HardwareBuffer::Usage final = static_cast<HardwareBuffer::Usage>(
																			 HardwareBuffer::HBU_STATIC_WRITE_ONLY | HardwareBuffer::HBU_DISCARDABLE);
			VertexDeclaration::VertexElementList::iterator v;
			for (v = destElems.begin(); v != destElems.end(); ++v)
			{
				VertexElement& destelem = *v;
				
				const VertexElement* srcelem = 
				vertexDeclaration->findElementBySemantic(
				destelem.getSemantic(), destelem.getIndex());
				HardwareVertexBufferSharedPtr srcbuf = 
				vertexBufferBinding->getBuffer(srcelem->getSource());
				if (srcbuf->getUsage() & HardwareBuffer::HBU_DYNAMIC)
				{
					final = static_cast<HardwareBuffer::Usage>(
						final & ~HardwareBuffer::HBU_STATIC);
					final = static_cast<HardwareBuffer::Usage>(
						final | HardwareBuffer::HBU_DYNAMIC);
				}
				if (!(srcbuf->getUsage() & HardwareBuffer::HBU_WRITE_ONLY))
				{
					final = static_cast<HardwareBuffer::Usage>(
						final & ~HardwareBuffer::HBU_WRITE_ONLY);
				}
				if (!(srcbuf->getUsage() & HardwareBuffer::HBU_DISCARDABLE))
				{
					final = static_cast<HardwareBuffer::Usage>(
						final & ~HardwareBuffer::HBU_DISCARDABLE);
				}
			}
			usages.push_back(final);
		}
		
		reorganiseBuffers(newDeclaration, usages, mgr);
	}
	
	void VertexData::closeGapsInBindings(void)
	{
		if (!vertexBufferBinding->hasGaps())
			return;
		
		const VertexDeclaration::VertexElementList& allelems = 
		vertexDeclaration->getElements();
		for (ai = allelems.begin(); ai != allelems.end(); ++ai)
		{
			const VertexElement& elem = *ai;
			if (!vertexBufferBinding->isBufferBound(elem.getSource()))
			{
				PVM_EXCEPT("No buffer is bound to that element source.",
						   "VertexData::closeGapsInBindings");
			}
		}
		
		VertexBufferBinding::BindingIndexMap bindingIndexMap;
		vertexBufferBinding->closeGaps(bindingIndexMap);
		
		unsigned short elemIndex = 0;
		for (ai = allelems.begin(); ai != allelems.end(); ++ai, ++elemIndex)
		{
			const VertexElement& elem = *ai;
			VertexBufferBinding::BindingIndexMap::const_iterator it = 
			bindingIndexMap.find(elem.getSource());
			assert(it != bindingIndexMap.end());
			ushort targetSource = it->second;
			if (elem.getSource() != targetSource)
			{
				vertexDeclaration->modifyElement(elemIndex,
						targetSource, elem.getOffset(), elem.getType(),
												 elem.getSemantic(), elem.getIndex());
			}
		}
	}
	
	void VertexData::removeUnusedBuffers(void)
	{
		set<ushort> usedBuffers;
		
		const VertexDeclaration::VertexElementList& allelems = 
		vertexDeclaration->getElements();
		VertexDeclaration::VertexElementList::const_iterator ai;
		for (ai = allelems.begin(); ai != allelems.end(); ++ai)
		{
			const VertexElement& elem = *ai;
			usedBuffers.insert(elem.getSource());
		}
		
		ushort count = vertexBufferBinding->getLastBoundIndex();
		for (ushort index = 0; index < count; ++index)
		{
			if (usedBuffers.find(index) == usedBuffers.end() &&
				vertexBufferBinding->isBufferBound(index))
			{
				vertexBufferBinding->unsetBinding(index);
			}
		}
		
		closeGapsInBindings();
	}
	
	void VertexData::convertPackedColour(
	VertexElementType srcType, VertexElementType destType)
	{
		if (destType != VET_COLOUR_ABGR && destType != VET_COLOUR_ARGB)
		{
			PVM_EXCEPT("Invalid destType parameter",
					   "VertexData::convertPackedColour");
		}
		if (srcType != VET_COLOUR_ABGR && srcType != VET_COLOUR_ARGB)
		{
			PVM_EXCEPT("Invalid srcType parameter", 
					   "VertexData::convertPackedColour");
		}
		
		const VertexBufferBinding::VertexBufferBindingMap& bindMap = 
		vertexBufferBinding->getBindings();
		VertexBufferBinding::VertexBufferBindingMap::const_iterator bindi;
		for (bindi = bindMap.begin(); bindi != bindMap.end(); ++bindi)
		{
			VertexDeclaration::VertexElementList elems = 
			vertexDeclaration->findElementsBySource(bindi->first);
			bool conversionNeeded = false;
			VertexDeclaration::VertexElementList::iterator elemi;
			for (elemi = elems.begin(); elemi != elems.end(); ++elemi)
			{
				VertexElement& elem = *elemi;
				if (elem.getType() == VET_COLOUR ||
					((elem.getType() == VET_COLOUR_ABGR || elem.getType() == VET_COLOUR_ARGB)
					 && elem.getType() != destType))
				{
					conversionNeeded = true;
				}
			}
			
			if (conversionNeeded)
			{
				void* pBase = bindi->second->lock(HardwareBuffer::HBL_NORMAL);
				
				for (size_t v = 0; v < bindi->second->getNumVertices(); ++v)
				{
					for (elemi = elems.begin(); elemi != elems.end(); ++elemi)
					{
						VertexElement& elem = *elemi;
						VertexElementType currType = (elem.getType() == VET_COLOUR) ?
						srcType : elem.getType();
						if (elem.getType() == VET_COLOUR ||
							((elem.getType() == VET_COLOUR_ABGR || elem.getType() == VET_COLOUR_ARGB)
							 && elem.getType() != destType))
						{
							uint32* pRGBA;
							elem.baseVertexPointerToElement(pBase, &pRGBA);
							VertexElement::convertColourValue(currType, destType, pRGBA);
						}
					}
					pBase = static_cast<void*>(
											   static_cast<char*>(pBase) + bindi->second->getVertexSize()
					);
				}
				bindi->second->unlock();
				
				const VertexDeclaration::VertexElementList& allelems = 
				vertexDeclaration->getElements();
				VertexDeclaration::VertexElementList::const_iterator ai;
				unsigned short elemIndex = 0;
				for (ai = allelems.begin(); ai != allelems.end(); ++ai, ++elemIndex)
				{
					const VertexElement& elem = *ai;
					if (elem.getType() == VET_COLOUR ||
						((elem.getType() == VET_COLOUR_ABGR || elem.getType() == VET_COLOUR_ARGB)
						 && elem.getType() != destType))
					{
						vertexDeclaration->modifyElement(elemIndex,
														 elem.getSource(), elem.getOffset(), destType,
														 elem.getSemantic(), elem.getIndex());
					}
				}
			}
		}
	}
	
	ushort VertexData::allocateHardwareAnimationElements(ushort count, bool animateNormals)
	{
		unsigned short texCoord = vertexDeclaration->getNextFreeTextureCoordinate();
		unsigned short freeCount = (ushort)(PVM_MAX_TEXTURE_COORD_SETS - texCoord);
		if (animateNormals)
			freeCount /= 2;
		
		unsigned short supportedCount = std::min(freeCount, count);
		
		for (size_t c = hwAnimationDataList.size(); c < supportedCount; ++c)
		{
			HardwareAnimationData data;
			data.targetBufferIndex = vertexBufferBinding->getNextIndex();
			vertexDeclaration->addElement(data.targetBufferIndex, 0, VET_FLOAT3, VES_TEXTURE_COORDINATES, texCoord++);
			if (animateNormals)
				vertexDeclaration->addElement(data.targetBufferIndex, sizeof(float) * 3, VET_FLOAT3, VES_TEXTURE_COORDINATES, texCoord++);
			hwAnimationDataList.push_back(data);
		}
		
		return supportedCount;
	}
	
	IndexData::IndexData()
	{
		indexCount = 0;
		indexStart = 0;
	}
	
	IndexData::~IndexData()
	{
	}
	
	IndexData* IndexData::clone(bool copyData, HardwareBufferManagerBase* mgr) const
	{
		HardwareBufferManagerBase* pManager = mgr ? mgr : HardwareBufferManager::getSingletonPtr();
		IndexData* dest = new IndexData();
		if (indexBuffer.get())
		{
			if (copyData)
			{
				dest->indexBuffer = pManager->createIndexBuffer(indexBuffer->getType(), indexBuffer->getNumIndexes(),
																indexBuffer->getUsage(), indexBuffer->hasShadowBuffer());
				dest->indexBuffer->copyData(*indexBuffer, 0, 0, indexBuffer->getSizeInBytes(), true);
			}
			else
			{
				dest->indexBuffer = indexBuffer;
			}
		}
		dest->indexCount = indexCount;
		dest->indexStart = indexStart;
		return dest;
	}
	
	class Triangle
	{
	public:
		enum EdgeMatchType
		{
			AB, BC, CA, ANY, NONE
		};
		
		uint32 a, b, c;
		
		inline Triangle()
		{
		}
		
		inline Triangle(uint32 ta, uint32 tb, uint32 tc)
		: a(ta), b(tb), c(tc)
		{
		}
		
		inline Triangle(uint32 t[3])
		: a(t[0]), b(t[1]), c(t[2])
		{
		}
		
		inline Triangle(const Triangle& t)
		: a(t.a), b(t.b), c(t.c)
		{
		}
		
		inline bool sharesEdge(const Triangle& t) const
		{
			return (a == t.a && b == t.c ||
					a == t.b && b == t.a ||
					a == t.c && b == t.b ||
					b == t.a && c == t.c ||
					b == t.b && c == t.a ||
					b == t.c && c == t.b ||
					c == t.a && a == t.c ||
					c == t.b && a == t.a ||
					c == t.c && a == t.b);
		}
		
		inline bool sharesEdge(const uint32 ea, const uint32 eb, const Triangle& t) const
		{
			if (ea == t.a && eb == t.c ||
				ea == t.b && eb == t.a ||
				ea == t.c && eb == t.b);
		}
		
		inline bool sharesEdge(const EdgeMatchType edge, const Triangle& t) const
		{
			if (edge == AB)
				return sharesEdge(a, b, t);
			else if (edge == BC)
				return sharesEdge(b, c, t);
			else if (edge == CA)
				return sharesEdge(c, a, t);
			else
				return (edge == ANY) == sharesEdge(t);
		}
		
		inline EdgeMatchType endoSharedEdge(const Triangle& t) const
		{
			if (sharesEdge(a, b, t)) return AB;
			if (sharesEdge(b, c, t)) return BC;
			if (sharesEdge(c, a, t)) return CA;
			return NONE;
		}
		
		inline EdgeMatchType exoSharedEdge(const Triangle& t) const
		{
			return t.endoSharedEdge(*this);
		}
		
		inline void shiftClockwise()
		{
			uint32 t = a;
			a = c;
			c = b;
			b = t;
		}
		
		inline void shiftCounterClockwise()
		{
			uint32 t = a;
			a = b;
			b = c;
			c = t;
		}
	};
	
	void IndexData::optimiseVertexCacheTriList(void)
	{
		if (indexBuffer->isLocked()) return;
		
		void* buffer = indexBuffer->lock(HardwareBuffer::HBL_NORMAL);
		
		Triangle* triangles;
		uint32* dest;
		
		size_t nIndexes = indexCount;
		size_t nTriangles = nIndexes / 3;
		size_t i, j;
		uint16* source = 0;
		
		if (indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT)
		{
			triangles = new Triangle[nTriangles];
			source = (uint16*)buffer;
			dest = (uint32*)triangles;
			for (i = 0; i < nIndexes; ++i) dest[i] = source[i];
		}
		else
			triangles = (Triangle*)buffer;
		
		uint32* destlist = new uint32[nTriangles];
		unsigned char* visited = new unsigned char[nTriangles];
		
		for (i = 0; i < nTriangles; ++i) visited[i] = 0;
		
		uint32 start = 0, ti = 0, destcount = 0;
		
		bool found = false;
		for (i = 0; i < nTriangles; ++i)
		{
			if (found)
				found = false;
			else
			{
				while (visited[start++]);
				ti = start - 1;
			}
			
			destlist[destcount++] = ti;
			visited[ti] = 1;
			
			for (j = start; j < nTriangles; ++j)
			{
				if (visited[j]) continue;
				
				if (triangles[ti].sharesEdge(triangles[j]))
				{
					found = true;
					ti = static_cast<uint32>(j);
					break;
				}
			}
		}
		
		if (indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT)
		{
			j = 0;
			for (i = 0; i < nTriangles; ++i)
			{
				Triangle* t = &triangles[destlist[i]];
				source[j++] = (uint16)t->a;
				source[j++] = (uint16)t->b;
				source[j++] = (uint16)t->c;
			}
			
			delete[] triangles;
		}
		else
		{
			uint32* reflist = new uint32[nTriangles];
			
			for (i = 0; i < nTriangles; ++i)
				reflist[destlist[i]] = static_cast<uint32>(i);
			
			for (i = 0; i < nTriangles; ++i)
			{
				j = destlist[i];
				if (i == j) continue;
				
				Triangle t = triangles[i];
				triangles[i] = triangles[j];
				triangles[j] = t;
				
				destlist[reflist[i]] = static_cast<uint32>(j);
			}
			
			delete[] reflist;
		}
		
		delete[] destlist;
		delete[] visited;
		
		indexBuffer->unlock();
	}
	
	void VertexCacheProfiler::profile(const HardwareIndexBufferSharedPtr& indexBuffer)
	{
		if (indexBuffer->isLocked()) return;
		
		uint16* shortbuffer = (uint16*)indexBuffer->lock(HardwareBuffer::HBL_READ_ONLY);
		
		if (indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT)
			for (unsigned int i = 0; i < indexBuffer->getNumIndexes(); ++i)
				inCache(shortbuffer[i]);
		else
		{
			uint32* buffer = (uint32*)shortbuffer;
			for (unsigned int i = 0; i < indexBuffer->getNumIndexes(); ++i)
				inCache(buffer[i]);
		}
		
		indexBuffer->unlock();
	}
	
	bool VertexCacheProfiler::inCache(unsigned int index)
	{
		for (unsigned int i = 0; i < buffersize; ++i)
		{
			if (index == cache[i])
			{
				hit++;
				return true;
			}
		}
		
		miss++;
		cache[tail++] = index;
		tail %= size;
		
		if (buffersize < size) buffersize++;
		
		return false;
	}
}
